<?php
/**
 * Created by PhpStorm.
 * User: 86151
 * Date: 2024/3/6
 * Time: 11:24
 */

namespace app\admin\model;
use think\Config;
use think\Db;
use think\Model;

class Wxpay extends Model
{
    //订单查询接口
    public static function ordercx($out_trade_no){
        $params = [
            'appid' => Config::get("wechatPay.appid"), //公众号appId
            'mch_id' =>Config::get("wechatPay.mchid"), //商户号id
            'nonce_str' => self::createNonceStr(), //生成随机字符串
            'out_trade_no' => $out_trade_no, // 订单号
            'sign_type' => 'MD5', // 签名类型
        ];
        $params['sign'] = self::sign($params, Config::get("wechatPay.merchantV2")); // 签名
        $xml = self::arrayToXml($params);
        $url = "https://api.mch.weixin.qq.com/pay/orderquery";
        $dataxml = self::postXmlCurl($xml,$url);
        //后台POST微信传参地址  同时取得微信返回的参数
        $objectxml = (array)simplexml_load_string($dataxml, 'SimpleXMLElement', LIBXML_NOCDATA);
        return $objectxml;
    }

    public static function sign($params, $key) {
        ksort($params);
        $string1 = '';
        foreach ($params as $k => $v) {
            if ($k != 'sign' && $v != '' && !is_array($v)) {
                $string1 .= $k . '=' . $v . '&';
            }
        }
        $string1 .= 'key=' . $key;
        $sign = strtoupper(md5($string1));
        return $sign;
    }

    public  static  function xmlToArray($xml) {
        // 简单的错误判断
        if (!$xml) {
            return false;
        }
        // 将XML转为SimpleXML对象
        libxml_disable_entity_loader(true);
        $obj = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if (!$obj) {
            return false;
        }
        // 转为数组
        $data = json_decode(json_encode($obj), true);
        return $data;
    }

    //退款
    public static function doRefund($totalFee,$refundFee,$refundNo,$wxOrderNo='', $refundDesc='') {
        //所需数据
        $data = [
            'appid' => Config::get("wechatPay.appid"), //公众号appId
            'mch_id' =>Config::get("wechatPay.mchid"), //商户号id
            'nonce_str' => self::createNonceStr(), //生成随机字符串
            'total_fee' => $totalFee* 100, //订单金额  单位 转为分
            'refund_fee' => $refundFee * 100, //退款金额 单位 转为分
            'sign_type' => 'MD5', //签名类型 支持HMAC-SHA256和MD5，默认为MD5
            'transaction_id' => $wxOrderNo, //微信订单号
            'out_refund_no' => $refundNo, //商户退款单号
            'refund_desc' => $refundDesc, //退款原因（选填）
        ];
        //生成签， $this->API_KEY 商户号API秘钥
        $data['sign'] = self::getRefundSign($data,Config::get("wechatPay.merchantV2"));
        //发送xml数据
        $responseXml = self::curlPost('https://api.mch.weixin.qq.com/secapi/pay/refund', self::arrayToXml($data));
        $unifiedOrder = simplexml_load_string($responseXml, 'SimpleXMLElement', LIBXML_NOCDATA);
        if($unifiedOrder === false) {
            //写日志查看错误
            //'微信订单号：'.$wxOrderNo.'，微信退款xml解析错误'
            return 1;
        }
        if($unifiedOrder->return_code != 'SUCCESS') {
//            $error = $unifiedOrder->return_msg.'1';
//            //写日志查看错误
            return 2;
        }
        if($unifiedOrder->result_code != 'SUCCESS') {
//            $error = $unifiedOrder->err_code_des.'2';
            return 3;
        }
        if(empty($error)){
            return 0;
        };
    }

    //支付
    public static function wx_pay_zh($res,$notify_urls,$Info,$openid){
        $appid =  Config::get('wechatPay.appid');
        $mch_id =  Config::get('wechatPay.mchid');
        $key = Config::get('wechatPay.merchantV2');
        $body = $Info; //商品描述
        $out_trade_no = $res['order_sn'];//商户订单号
        $nonce_str=MD5(md5($out_trade_no).md5('ZJ'));//随机字符串
        $total_fee = $res['order_amount']*100; //金额
//        $openid = $user_info['openid'];
        $spbill_create_ip = $_SERVER['REMOTE_ADDR']; //终端ip
        $notify_url = $notify_urls; //回调地址(:本站改变订单状态不在异步回调处理)
        $trade_type = 'JSAPI';
        //以上参数接收不必纠结，按照正常接收就行，相信大家都看得懂
        //对参数按照key=value的格式，并按照参数名ASCII字典序排序生成字符串
        $signA = "appid=$appid&body=$body&mch_id=$mch_id&nonce_str=$nonce_str&notify_url=$notify_url&openid=$openid&out_trade_no=$out_trade_no&spbill_create_ip=$spbill_create_ip&total_fee=$total_fee&trade_type=$trade_type";
        $strSignTmp = $signA."&key=".$key; //拼接字符串
        $sign = strtoupper(MD5($strSignTmp)); // MD5 后转换成大写
        $post_data = "<xml>
                       <appid>$appid</appid>
                       <body>$body</body>
                       <mch_id>$mch_id</mch_id>
                       <nonce_str>$nonce_str</nonce_str>
                       <notify_url>$notify_url</notify_url>
                       <openid>$openid</openid>
                       <out_trade_no>$out_trade_no</out_trade_no>
                       <spbill_create_ip>$spbill_create_ip</spbill_create_ip>
                       <total_fee>$total_fee</total_fee>
                       <trade_type>$trade_type</trade_type>
                       <sign>$sign</sign>
                   </xml>";//拼接成XML 格式
//        file_put_contents("33-6.txt",print_r($post_data,true));
        $url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//微信传参地址
//        file_put_contents("55.txt",print_r($url,true));

        $dataxml = self::http_post_ed($url,$post_data); //后台POST微信传参地址  同时取得微信返回的参数，http_post方法请看下文
//        file_put_contents("3.txt",print_r($dataxml,true));
        $objectxml = (array)simplexml_load_string($dataxml, 'SimpleXMLElement', LIBXML_NOCDATA); //将微信返回的XML 转换成数组
//        file_put_contents("4.txt",print_r($objectxml,true));
        if($objectxml)  {
//            $this->error('获取失败',$objectxml);
            if($objectxml['return_code'] == 'SUCCESS' && $objectxml['result_code'] =='SUCCESS'){
                $time = time();
                //根据prepay_id生成公众号支付参数
                $re['appId'] = $appid;
                $re['timeStamp'] = "$time";
                $re['nonceStr'] = "$nonce_str";
                $re['package'] = "prepay_id=".$objectxml['prepay_id'];
                $re['signType'] = "MD5";
                $re['return_code'] = $objectxml['return_code'];
                $re['paySign'] = strtoupper(md5("appId=$appid&nonceStr=$nonce_str&package=prepay_id={$objectxml['prepay_id']}&signType=MD5&timeStamp=$time&key=".$key));
                return $re;
            }
            if($objectxml['result_code'] == 'FAIL'){
                $err_code_des = $objectxml['err_code_des'];
                echo json_encode(array('code'=>0, 'msg'=>$err_code_des));die;
            }
        }else{
            return false;
        }
    }
    public static function http_post_ed($url, $data, $type="POST") {
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HEADER,0);
        curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $type );
        if (ini_get('open_basedir') == '' && ini_get('safe_mode' == 'Off')) {
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        }
        curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $data );

        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $tmpInfo = curl_exec($ch);

        curl_close($ch);
        return $tmpInfo;
    }
    public static  function createNoncestrs( $length = 32 ){
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str ="";
        for ( $i = 0; $i < $length; $i++ )  {
            $str.= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }

    public static function postXmlCurl($xml,$url,$second = 30){
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);
        curl_setopt($ch,CURLOPT_URL, $url);
        curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
        curl_setopt($ch,CURLOPT_SSL_VERIFYHOST,FALSE);
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if($data){
            curl_close($ch);
            return $data;
        }else{
            $error = curl_errno($ch);
            curl_close($ch);
            echo "curl出错，错误码:$error"."<br>";
        }
    }

    /*
* 组成微信需要XML数据
*/
    public static function arrayToXml($arr) {
        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }
    /*
     * 生成随机字符串方法
     */
    public static function createNonceStr($length = 32){
        $chars = "abcdefghijklmnopqrstuvwxyz0123456789";
        $str = "";
        for ( $i = 0; $i < $length; $i++ ) {
            $str .= substr($chars, mt_rand(0, strlen($chars)-1), 1);
        }
        return $str;
    }
    /*
     * 生成退款使用签名
     */
    protected static function getRefundSign($params, $key) {
        ksort($params, SORT_STRING);
        $unSignParaString = self::formatQueryParaMap($params, false);
        $signStr = strtoupper(md5($unSignParaString . "&key=" . $key));

        return $signStr;
    }
    /*
     * 将退款需要的信息组成签名算法
     */
    protected static function formatQueryParaMap($paraMap, $urlEncode = false) {
        $buff = "";
        ksort($paraMap);
        foreach ($paraMap as $k => $v) {
            if (null != $v && "null" != $v) {
                if ($urlEncode) {
                    $v = urlencode($v);
                }
                $buff .= $k . "=" . $v . "&";
            }
        }
        $reqPar = '';
        if (strlen($buff) > 0) {
            $reqPar = substr($buff, 0, strlen($buff) - 1);
        }

        return $reqPar;
    }
    /*
     * 微信退款使用curl方法
     */
    public static function curlPost($url = '', $postData = '', $options = array()) {
        if (is_array($postData)) {
            $postData = http_build_query($postData);
        }
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_POST, 1);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //设置cURL允许执行的最长秒数
        if (!empty($options)) {
            curl_setopt_array($ch, $options);
        }
        //https请求 不验证证书和host
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
        //第一种方法，cert 与 key 分别属于两个.pem文件
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLCERT, ROOT_PATH.'vendor'.DS.'cert'.DS.'apiclient_cert.pem'); //证书绝对路径
        //默认格式为PEM，可以注释
        curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
        curl_setopt($ch, CURLOPT_SSLKEY, ROOT_PATH.'vendor'.DS.'cert'.DS.'apiclient_key.pem');//证书绝对路径
        //第二种方式，两个文件合成一个.pem文件
        //curl_setopt($ch,CURLOPT_SSLCERT,getcwd().'/all.pem');
        $data = curl_exec($ch);
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            echo "curl出错，错误码: ".$error."<br>";
            curl_close($ch);
            return false;
        }
    }
}